<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>SSE Markdown Viewer - Vue3</title>
    <!-- Vue 3 -->
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/3.5.22/vue.global.min.js"></script>
    <!-- KaTeX支持数学公式 -->
    <link
      rel="stylesheet"
      href="https://cdn.bootcdn.net/ajax/libs/KaTeX/0.16.9/katex.min.css"
    />
    <script
      defer
      src="https://cdn.bootcdn.net/ajax/libs/KaTeX/0.16.9/katex.min.js"
    ></script>
    <script
      defer
      src="https://cdn.bootcdn.net/ajax/libs/KaTeX/0.16.9/contrib/auto-render.min.js"
    ></script>
    <!-- 引入marked库用于Markdown解析 -->
    <script src="https://cdn.bootcdn.net/ajax/libs/marked/16.2.1/lib/marked.umd.js"></script>
    <style>
      /* 基础样式 */
      body {
        font-family:
          -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC',
          'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', Helvetica,
          Arial, sans-serif;
        line-height: 1.6;
        color: #333;
        max-width: 800px;
        margin: 0 auto;
        padding: 20px;
        background-color: #f5f5f5;
      }

      /* 容器样式 */
      .container {
        background-color: white;
        border-radius: 8px;
        box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
        padding: 20px;
        margin-top: 20px;
      }

      /* 头部样式 */
      header {
        text-align: center;
        margin-bottom: 30px;
      }
      h1 {
        color: #2c3e50;
      }

      /* Markdown内容样式 */
      .markdown-content {
        padding: 20px;
        background-color: #fafafa;
        border-radius: 4px;
        min-height: 200px;
        max-height: 600px;
        overflow-y: auto;
        font-size: 16px;
      }

      /* 状态信息样式 */
      .status {
        margin-top: 20px;
        padding: 10px;
        border-radius: 4px;
        background-color: #e3f2fd;
        color: #1976d2;
        font-size: 14px;
      }

      /* 加载动画样式 */
      .loading {
        display: inline-block;
        width: 20px;
        height: 20px;
        border: 3px solid rgba(0, 0, 0, 0.1);
        border-radius: 50%;
        border-top-color: #3498db;
        animation: spin 1s ease-in-out infinite;
      }

      @keyframes spin {
        to {
          transform: rotate(360deg);
        }
      }

      /* 输入框样式 */
      .query-input {
        width: 100%;
        padding: 10px;
        font-size: 16px;
        border: 1px solid #ddd;
        border-radius: 4px;
        margin-bottom: 10px;
        box-sizing: border-box;
      }

      /* 按钮样式 */
      .submit-btn {
        padding: 10px 20px;
        background-color: #3498db;
        color: white;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        font-size: 16px;
        transition: background-color 0.3s;
      }

      .submit-btn:hover {
        background-color: #2980b9;
      }

      /* 断开连接按钮 */
      .disconnect-btn {
        padding: 8px 16px;
        background-color: #e74c3c;
        color: white;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        font-size: 14px;
        margin-top: 10px;
        margin-right: 10px;
        transition: background-color 0.3s;
      }

      .disconnect-btn:hover {
        background-color: #c0392b;
      }

      /* 重新连接按钮 */
      .reconnect-btn {
        padding: 8px 16px;
        background-color: #2ecc71;
        color: white;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        font-size: 14px;
        margin-top: 10px;
        transition: background-color 0.3s;
      }

      .reconnect-btn:hover {
        background-color: #27ae60;
      }

      /* Markdown 样式增强 */
      .markdown-content h1,
      .markdown-content h2,
      .markdown-content h3 {
        color: #2c3e50;
        margin-top: 20px;
      }

      .markdown-content pre {
        background-color: #f1f1f1;
        padding: 15px;
        border-radius: 4px;
        overflow-x: auto;
      }

      .markdown-content code {
        background-color: #f1f1f1;
        padding: 2px 4px;
        border-radius: 3px;
        font-family: 'Courier New', Courier, monospace;
      }

      .markdown-content pre code {
        background-color: transparent;
        padding: 0;
      }

      .markdown-content blockquote {
        border-left: 4px solid #3498db;
        margin-left: 0;
        padding-left: 15px;
        color: #666;
      }

      .markdown-content table {
        width: 100%;
        border-collapse: collapse;
        margin: 20px 0;
      }

      .markdown-content table th,
      .markdown-content table td {
        border: 1px solid #ddd;
        padding: 8px 12px;
      }

      .markdown-content table th {
        background-color: #f2f2f2;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <div class="container">
        <header>
          <h1>SSE Markdown Viewer - Vue3</h1>
        </header>

        <div class="input-section">
          <input
            type="text"
            v-model="query"
            class="query-input"
            placeholder="输入查询内容，例如：nestjs简介"
            @keyup.enter="connectSSE"
          />
          <button
            @click="connectSSE"
            class="submit-btn"
            :disabled="isConnected"
          >
            发送查询
          </button>
        </div>

        <div class="status">
          <span>{{ statusText }}</span>
          <span v-if="isLoading" class="loading"></span>
        </div>

        <div
          class="markdown-content"
          v-html="renderedMarkdown"
          ref="markdownContainer"
        ></div>

        <div class="controls">
          <button
            v-if="isConnected"
            @click="disconnectSSE"
            class="disconnect-btn"
          >
            断开连接
          </button>
          <button @click="connectSSE" class="reconnect-btn">重新连接</button>
        </div>
      </div>
    </div>

    <script>
      const {
        createApp,
        ref,
        reactive,
        computed,
        onMounted,
        onUnmounted,
        nextTick,
      } = Vue;

      const app = createApp({
        setup() {
          // 响应式数据
          const query = ref('nestjs简介');
          const rawMarkdown = ref('');
          const statusText = ref('等待连接...');
          const isLoading = ref(false);
          const isConnected = ref(false);
          const hasConnected = ref(false);
          const markdownContainer = ref(null);
          let evtSource = null;

          // 计算属性：渲染后的Markdown内容
          const renderedMarkdown = computed(() => {
            if (!rawMarkdown.value) return '';
            // 使用marked库渲染Markdown
            try {
              return marked.parse(rawMarkdown.value);
            } catch (error) {
              console.error('Markdown渲染错误:', error);
              return rawMarkdown.value;
            }
          });

          // 连接SSE服务器
          const connectSSE = async () => {
            if (!query.value.trim() || isConnected.value) return;

            // 清理之前的连接
            if (evtSource) {
              evtSource.close();
            }

            // 重置状态
            rawMarkdown.value = '';
            statusText.value = '连接中...';
            isLoading.value = true;

            try {
              // 创建新的EventSource连接
              evtSource = new EventSource(
                `http://127.0.0.1:3333/api/openai/question?query=${encodeURIComponent(query.value.trim())}`,
              );

              // 连接成功
              evtSource.onopen = () => {
                statusText.value = '已连接';
                isLoading.value = false;
                isConnected.value = true;
                hasConnected.value = true;
                console.log('SSE连接已建立');
              };

              // 接收消息
              evtSource.onmessage = (e) => {
                try {
                  // 解析接收到的数据
                  const data = JSON.parse(e.data);
                  console.log('接收到数据:', data);

                  // 检查数据格式是否符合要求
                  if (data.content) {
                    // 将content追加到Markdown内容
                    rawMarkdown.value += data.content;

                    // 等待DOM更新后滚动到底部并渲染数学公式
                    nextTick(() => {
                      scrollToBottom();
                      renderMathInElement();
                    });
                  }

                  // 如果接收到结束标志
                  if (data.finish_reason === 'stop') {
                    statusText.value = '接收完成';
                    isLoading.value = false;
                    console.log('接收完成:', data.finish_reason);
                    disconnectSSE();
                  }
                } catch (error) {
                  console.error('解析数据错误:', error);
                  // 尝试直接将数据作为文本处理
                  rawMarkdown.value += e.data;
                  nextTick(() => {
                    scrollToBottom();
                  });
                }
              };

              // 连接错误
              evtSource.onerror = (error) => {
                console.error('SSE连接错误:', error);
                statusText.value = '连接错误';
                isLoading.value = false;
                isConnected.value = false;
              };
            } catch (error) {
              console.error('创建EventSource失败:', error);
              statusText.value = '连接失败';
              isLoading.value = false;
              isConnected.value = false;
            }
          };

          // 断开连接
          const disconnectSSE = () => {
            if (evtSource) {
              evtSource.close();
              evtSource = null;
            }
            statusText.value = '已断开连接';
            isLoading.value = false;
            isConnected.value = false;
          };

          // 滚动到底部
          const scrollToBottom = () => {
            if (markdownContainer.value) {
              markdownContainer.value.scrollTop =
                markdownContainer.value.scrollHeight;
            }
          };

          // 渲染数学公式
          const renderMathInElement = () => {
            try {
              // 确保KaTeX已加载
              if (window.renderMathInElement && markdownContainer.value) {
                window.renderMathInElement(markdownContainer.value, {
                  delimiters: [
                    { left: '$$', right: '$$', display: true },
                    { left: '$', right: '$', display: false },
                  ],
                });
              }
            } catch (error) {
              console.warn('数学公式渲染失败:', error);
            }
          };

          // 页面关闭时断开连接
          onUnmounted(() => {
            disconnectSSE();
          });

          // 配置marked选项
          onMounted(() => {
            // 设置marked的选项
            marked.setOptions({
              breaks: true,
              gfm: true,
              headerIds: true,
              mangle: false,
            });
          });

          // 暴露给模板
          return {
            query,
            statusText,
            isLoading,
            isConnected,
            hasConnected,
            renderedMarkdown,
            markdownContainer,
            connectSSE,
            disconnectSSE,
          };
        },
      });

      app.mount('#app');
    </script>
  </body>
</html>
